﻿using System;

namespace Bravura.Native.PluginBase
{
	/// <summary>
	/// Does Fourier analysis and synthesis.
	/// </summary>
	public static class Fourier
	{
		/// <summary>
		/// Constant value of 2.0 * Math.PI.
		/// </summary>
		public static double TwoPi = 2.0 * Math.PI;

		/// <summary>
		/// Performs a DFT analysis of a waveform.
		/// </summary>
		public static Complex[] Analyze(double[] input)
		{
			// number of input values (time domain)
			int size = input.Length;

			// number of output values (frequency domain)
			int num = size / 2 + 1;

			Complex[] output = new Complex[num];

			double mul = 2.0 / size;

			for (int i = 0; i < num; i++) // frequency domain iterator
			{
				Complex temp = new Complex();
				double w = TwoPi * i / size;
				for (int k = 0; k < size; k++) // time domain iterator
				{
					double a = k * w;
					temp.Real += input[k] * Math.Cos(a);
					temp.Imag += input[k] * Math.Sin(a);
				}
				temp.Real = temp.Real * mul;
				temp.Imag = temp.Imag * mul;
				output[i] = temp;
			}

			return output;
		}

		/// <summary>
		/// Performs a DFT synthesis of a waveform analysis.
		/// </summary>
		public static double[] Synthesize(Complex[] input)
		{
			return Synthesize(input, (input.Length-1) * 2, input.Length);
		}

		/// <summary>
		/// Performs a DFT synthesis of a waveform analysis, 
		/// producing a waveform of the specified size.
		/// </summary>
		public static double[] Synthesize(Complex[] input, int size)
		{
			return Synthesize(input, size, input.Length);
		}

		/// <summary>
		/// Performs a DFT synthesis of a waveform analysis, 
		/// producing a waveform of the specified size, 
		/// and limits the number of partials to use.
		/// </summary>
		public static double[] Synthesize(Complex[] input, int size, int num)
		{
			double[] output = new double[size];

			// do not generate a higher frequency than half the output buffer size
			if (num > size / 2 + 1)
			{
				num = size / 2 + 1;
			}

			// ensure we don't attempt to read outside the input array
			if (num > input.Length)
			{
				num = input.Length;
			}

			for (int i = 0; i < size; i++) // time domain iterator
			{
				double temp = 0.0;
				double w = TwoPi * i / size;
				for (int k = 0; k < num; k++) // frequency domain iterator
				{
					double a = k * w;
					temp += input[k].Real * Math.Cos(a);
					temp += input[k].Imag * Math.Sin(a);
				}
				output[i] = temp;
			}

			return output;
		}
	}

	/// <summary>
	/// Represents a complex number in rectangular form.
	/// </summary>
	public struct Complex
	{
		public double Real;
		public double Imag;

		public Polar ToPolar()
		{
			Polar p = new Polar();
			p.Mag = Math.Sqrt(Real * Real + Imag * Imag);
			p.Ang = Math.Atan2(Imag, Real);
			return p;
		}
	}

	/// <summary>
	/// Represents a complex value in polar form.
	/// </summary>
	public struct Polar
	{
		public double Mag;
		public double Ang;

		public Complex ToComplex()
		{
			Complex c = new Complex();
			c.Real = Mag * Math.Cos(Ang);
			c.Imag = Mag * Math.Sin(Ang);
			return c;
		}
	}
}
